package com.ruoyi.spspublic.util;

import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.spspublic.domain.*;
import com.ruoyi.spspublic.domain.dto.KpiBranchInadequateDto;
import com.ruoyi.spspublic.mapper.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class PartyKpiUtil {
    // 记录日志
    private static final Logger log = LoggerFactory.getLogger(PartyKpiUtil.class);
    // 获取redis中最大的签字日期
    @Resource
    private RedisCache redisCache;
    // kpi基本配置信息
    @Resource
    private TKpiBaseMapper tKpiBaseMapper;
    // kpi数据存储计算信息
    @Resource
    private TKpiDataMapper tKpiDataMapper;
    // kpi未进行计算的清单列表
    @Resource
    TKpiDataTempMapper tKpiDataTempMapper;
    // 组织不健全表
    @Resource
    TKpiSpecialtempMapper tKpiSpecialtempMapper;
    @Resource
    private TMisBaseJobMapper tMisBaseJobMapper;
    // 签字默认最晚天数
    private int maxDay = 16;

    // kpi规则-计算特例
    private List<TKpiBase> tKpiBaseSpecialList;

    /**
     * 对外接口：一. 实时计算KPI 签发时调用
     * 实时计算kpi指标，动态累计值到积分标；同时自动将未积分的数据写入临时表
     *
     * @param funNo  功能号
     * @param bussId 业务单据ID
     */
    public void kpiBaseInfo(String funNo, String bussId) {
        // 首先获取最迟的签字日期
        try {
            String maxDayStr = redisCache.getCacheObject("sys_config:sys.party.kpi.maxDay");
            maxDay = Integer.parseInt(maxDayStr);
        } catch (Exception e) {
            // 我只能默认16天了；如果没有配置
        }

        // 根据功能号获取对应的考核规则
        List<TKpiBase> tKpiBaseList = getKpiBaseWithFunno(funNo);
        // 遍历考核规则，获取对应的实际数值
        if (tKpiBaseList != null && tKpiBaseList.size() > 0) {
            // 循环处理多条规则
            tKpiBaseList.forEach(
                    kpiBase -> {
                        calculateKpi(kpiBase, bussId);
                    });
        }
    }

    /**
     * 对外接口： 二. 实时计算KPI 任务定时调用 特例计算 设定每天晚上10点调用最佳
     */
    public void kpiBaseInfo_task(String curYear) {
        // 因为需要跨年度考核，所以设置一个默认值
        TKpiData t310 = new TKpiData();
        // 获取年度和月份
        Calendar calendar = Calendar.getInstance();
        calendar.setTime( DateUtils.getNowDate());
        t310.setCtYear(curYear);
        t310.setCtMonth((calendar.get(Calendar.MONTH) + 1 > 9) ? ("" + (calendar.get(Calendar.MONTH) + 1)) : ("0" + (calendar.get(Calendar.MONTH) + 1)));
        // 首次计算，清空缓存的配置数据
        tKpiBaseSpecialList = null;

        // 01、组织不健全 - 分 规则id:18082000201 ok
        calculateBranchInadequate(curYear);
        // 02、党员发展  + - 分 规则id: 18082000320(+) 18082000334(0) 18082000336(-) ok
        calculatePartymanDevelop();
        // 03、未按期换届 -分（半年） 规则id:18082000310
        calculateYearPubic("18082000310", curYear, curYear, true, t310);
        // 04、未及时换届 -分（一年） 规则id:18082000312
        calculateYearPubic("18082000312", curYear, curYear, true, t310);
        // 05、双提升 规则id:18082000348
        calculateYearPubic("18082000348", curYear, curYear, true, null);
        // 06、双进入 规则id:18082000314  未开发
        calculateYearPubic("18082000314", curYear, curYear, true, null);
        // 07、党员学习 规则id:18082000304
        calculateYearPubic("18082000304", curYear, curYear, true, null);
    }

    /**
     * 对外接口： 三. 年底计算KPI 任务定时调用 年底计算 该方法在年初1月16日调用，数据截止到全年12月31日
     */
    public void kpiBaseInfo_year(String curYear) {
        // 首次计算，清空缓存的配置数据
        tKpiBaseSpecialList = null;
        // 设置默认值，就设置一个业务日期即可
        TKpiData defTKpiData = new TKpiData();
        defTKpiData.setDtBussDate(DateUtils.parseDate(curYear + "-12-31"));
        // 组织健全 +分 规则id:18082000172 组织健全
        calculateYearPubic("18082000172", curYear, curYear, true, defTKpiData);
        // 按期换届选举 +分 规则id:18082000306 按期换届选举
        calculateYearPubic("18082000306", curYear, curYear, true, defTKpiData);
        // 未讲党课 -分 规则id: 18082000277
        calculateYearPubic("18082000277", curYear, curYear, true, defTKpiData);
        // 08、每月是否按时交党费 -分 规则id:18082000303
        calculateYearPubic("18082000303", curYear, curYear, true, null);
    }

    /**
     * 私有：1. 实际计算kpi - kpiBaseInfo调用
     *
     * @param tKpiBase 考核规则
     * @param bussId   业务ID
     */
    private void calculateKpi(TKpiBase tKpiBase, String bussId) {
        String sql = tKpiBase.getCtSqlSelect().toLowerCase();
        String funNo = tKpiBase.getCtBussFunno();
        sql = sql.replace("[ct_id_p]", bussId);

        List<TKpiData> tKpiDataList = tKpiDataMapper.selectTKpiDataBySql(sql);
        tKpiDataList.forEach(
                tKpiData -> {
                    // ---------------把基础数据进行整合， 当然可能整合了也没啥用出 -------------------------------
                    // 业务类型 （01 党员 02 组织)
                    tKpiData.setCtType(tKpiBase.getCtType());
                    // 隶属功能和类型
                    tKpiData.setCtFunNo(tKpiBase.getCtBussFunno());
                    tKpiData.setCtFunType(tKpiBase.getCtBussType());
                    // 获取年度和月份
                    Calendar calendar = Calendar.getInstance();
                    calendar.setTime(tKpiData.getDtBussDate());
                    tKpiData.setCtYear("" + calendar.get(Calendar.YEAR));
                    tKpiData.setCtMonth(
                            (calendar.get(Calendar.MONTH) + 1 > 9)
                                    ? ("" + (calendar.get(Calendar.MONTH) + 1))
                                    : ("0" + (calendar.get(Calendar.MONTH) + 1)));
                    // 分值类型
                    tKpiData.setCtScoresType(tKpiBase.getCtScoreType());
                    // 计算类型
                    tKpiData.setCtCalculateType(tKpiBase.getCtCountType());
                    // 分值
                    tKpiData.setAmScores(tKpiBase.getAmScore());
                    // 规则id
                    tKpiData.setCtRuleId(tKpiBase.getCtId());
                    // 计算日期
                    tKpiData.setDtCalculateDate(DateUtils.getNowDate());
                    // 重要的是id
                    tKpiData.setCtId(IdUtils.simpleUUID());
                    // 好像没有啥用的字段，赋值“”
                    tKpiData.setCtYearmtype("");

                    // -----------------开始计算是否逾期，或者说为什么不能添加数据------------------------------
                    boolean returnVal = calculateKpiOverdue(tKpiData);
                    if (!returnVal) {
                        // 写入temp表， 记录原因为 签字超期
                        inserDataTemp(tKpiData, "1", "该单据签发时间逾期，超过" + maxDay + "天");
                        return;
                    }
                    // -----------------计算年度最大分，超过则不能添加-----------------------------------------
                    returnVal = calculateKpiCount(tKpiData, tKpiBase, "year");
                    if (!returnVal) {
                        // 写入temp表， 记录原因为 累计积分超过年度积分
                        inserDataTemp(tKpiData, "2", "该规则" + tKpiData.getCtRuleId() + "的年度积分已达标");
                        return;
                    }
                    // -----------------计算周期内是否超标-----------------
                    returnVal = calculateKpiCount(tKpiData, tKpiBase, "cycle");
                    if (!returnVal) {
                        // 写入temp表， 记录原因为 累计积分超过周期积分
                        inserDataTemp(
                                tKpiData,
                                "3",
                                "该规则" + tKpiData.getCtRuleId() + "的周期积分已达标！" + tKpiBase.getCtCycle());
                        return;
                    }
                    // 上面都通过了，存储数据了
                    tKpiDataMapper.insertTKpiData(tKpiData);
                });
    }

    /**
     * 私有：1.1 积分是否超过最大分值 calculateKpi 调用
     *
     * @param tKpiData
     * @param tKpiBase
     * @param calculateType year:年度积分 cycle:周期积分
     * @return
     */
    private boolean calculateKpiCount(TKpiData tKpiData, TKpiBase tKpiBase, String calculateType) {
        // 根据规则id查询当前年度的所有积分
        if (tKpiData == null || tKpiBase == null) return false;
        // 获取日期， 年度， 月度等
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(tKpiData.getDtBussDate());
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH) + 1;

        // 形成一个sql语句， 查询sum的总值
        // 基础sql --根据规则id查询
        String sql = "select ifnull( sum(am_scores),0) as am_sum from t_kpi_data where 1=1 ";
        // 规则id
        sql += " and ct_rule_id ='" + tKpiBase.getCtId() + "' ";
        // 单位或者个人id
        sql += " and ct_buss_partyorbranchid ='" + tKpiData.getCtBussPartyorbranchid() + "'";
        // 按照获取周期  匹配sql语句中的开始结束时间
        String dateS = "", dateE = "";
        if ("year".equals(calculateType)) {
            // 年度累计的，直接设置年类型即可
            dateS = "" + year + "-01-01 00:00:00";
            dateE = "" + year + "-12-31 23:59:59";
        } else {
            // 如果为 cycle 表示周期求和， 比较的值也不一样，后面计算。
            switch (tKpiBase.getCtCycle()) {
                case "年":
                    dateS = "" + year + "-01-01 00:00:00";
                    dateE = "" + year + "-12-31 23:59:59";
                    break;
                case "半年":
                    if (month > 6) {
                        dateS = year + "-07-01 00:00:00";
                        dateE = year + "-12-31 23:59:59";
                    } else {
                        dateS = year + "-01-01 00:00:00";
                        dateE = year + "-06-30 23:59:59";
                    }
                case "月":
                    dateS = getCurrentMonthDateBegin(tKpiData.getDtBussDate());
                    dateE = getCurrentMonthDateEnd(tKpiData.getDtBussDate());
                    break;
                case "季":
                    dateS = getCurrentQuarterDateBegin(tKpiData.getDtBussDate());
                    dateE = getCurrentQuarterDateEnd(tKpiData.getDtBussDate());
                    break;
                default:
                    break;
            }
        }
        sql += " and dt_buss_date >='" + dateS + "' and dt_buss_date <='" + dateE + "'";
        // 根据sql语句去查询目前总的分数
        Double count = tKpiDataMapper.selectTKpiDataCount(sql);
        // 获取年度最高分
        Double maxScore = tKpiBase.getAmHighestScore();
        if (!"year".equals(calculateType)) {
            // 根据周期去计算对应每个周期的最大值
            switch (tKpiBase.getCtCycle()) {
                case "半年":
                    maxScore = maxScore / 2;
                case "季":
                    maxScore = maxScore / 4;
                    break;
                case "月":
                    maxScore = maxScore / 12;
                    break;
            }
        }
        if (count < maxScore) {
            return true;
        }
        return false;
    }

    /**
     * 私有：1.2 会议签署日期是否超期 calculateKpi 调用
     * 根据更新日期，计算会议日期是否逾期了，如果超期返回false
     *
     * @param tKpiData kpi
     * @return
     */
    private boolean calculateKpiOverdue(TKpiData tKpiData) {
        // 把业务日期推送到月度的最后一天
        String tempDateS = getCurrentMonthDateEnd(tKpiData.getDtBussDate());
        Date bussDate = DateUtils.parseDate(tempDateS);
        // 比较两个日期

        try {
            int dayDiffer = getDayDiffer(bussDate, tKpiData.getDtAuditDate());
            if (dayDiffer < maxDay) return true;
        } catch (Exception e) {

        }
        return false;
    }

    /**
     * 私有：1.3 保存未获得积分的记录 t_kpi_data_temp表
     *
     * @param tKpiData
     */
    private void inserDataTemp(TKpiData tKpiData, String resonId, String resonDesc) {
        if (tKpiData == null) return;
        // 为了防止出错，先按照规则id和业务id去删除 (程序中只存在按照业务id删除，先按照这个吧，理论上需要增加规则id）
        tKpiDataTempMapper.deleteKpiDataByBusinessId(tKpiData.getCtBussId());
        // 新增数据
        TKpiDataTemp tKpiDataTemp = new TKpiDataTemp();
        tKpiDataTemp.setCtId(IdUtils.simpleUUID());
        tKpiDataTemp.setCtFunNo(tKpiData.getCtFunNo());
        tKpiDataTemp.setCtFunType(tKpiData.getCtFunType());
        tKpiDataTemp.setCtBussId(tKpiData.getCtBussId());
        tKpiDataTemp.setCtBussName(tKpiData.getCtBussName());
        tKpiDataTemp.setCtBussPartyorbranchid(tKpiData.getCtBussPartyorbranchid());
        tKpiDataTemp.setDtBussDate(tKpiData.getDtBussDate());
        tKpiDataTemp.setDtAuditDate(tKpiData.getDtAuditDate());
        tKpiDataTemp.setCtRuleId(tKpiData.getCtRuleId());
        tKpiDataTemp.setCtType(tKpiData.getCtType());
        tKpiDataTemp.setDtInsertDate(tKpiData.getDtCalculateDate());
        // 存储原因
        tKpiDataTemp.setCtEnd(resonId);
        tKpiDataTemp.setCtEndDesc(resonDesc);
        // 重复的先不管了，每次都是新增好了
        tKpiDataTempMapper.insertTKpiDataTemp(tKpiDataTemp);
    }

    /**
     * 私有： 2.1 实时计算-减分-组织不健全 规则id:18082000201
     */
    private void calculateBranchInadequate(String curYear) {
        // 拉取特例计算的所有规则
        getSpecicalList();
        String ctype = "18082000201";
        //如果规则表中不包含，则不进行运算
        List<TKpiBase> collect = tKpiBaseSpecialList.stream().filter(e -> e.getCtId() != null && e.getCtId().equals(ctype)).collect(Collectors.toList());
        if (collect == null || collect.size() < 1) {
            log.info("规则id:18082000201 组织不健全未启用");
            return;
        }
        if (collect.get(0).getAmScore() == 0) {
            log.info("规则id:18082000201 组织不健全积分为0，不计算");
            return;
        }
        // 查询不健全组织，且未超过90天的单位 （需要从一个临时表中拉取，因为这个是要计算90天的，每次执行找到就增加，减分后，删除 t_kpi_specialtemp
        TKpiSpecialtemp tKpiSpecialtemp = new TKpiSpecialtemp();
        List<TKpiSpecialtemp> tKpiSpecialtempList = tKpiSpecialtempMapper.selectTKpiSpecialtempList(tKpiSpecialtemp);
        // 当前日期/年份
        Date nowDate = DateUtils.getNowDate();
        // 实时按照年度拉取所有组织信息
        List<KpiBranchInadequateDto> kpiBranchInadequateDtoList = tKpiSpecialtempMapper.selectBranchInadequate(curYear);
        if (kpiBranchInadequateDtoList == null || kpiBranchInadequateDtoList.size() < 1) {
            log.info("规则id:18082000201 组织不健全 未查询到组织");
            return;
        }
        // 设置 党员数量， 班子最小人数， 班子目前人数
        Integer leader_ok = 0, leader_min = 0, partyman_count = 0;
        // 设置 描述变量
        String tempDesc = "";
        // 设置 考核标志 0不考核， 1考核
        String assessState = "0";
        for (int i = 0; i < kpiBranchInadequateDtoList.size(); i++) {
            assessState = "0";
            leader_min = kpiBranchInadequateDtoList.get(i).getAmLeaderMin();
            leader_ok = kpiBranchInadequateDtoList.get(i).getAmLeaderOk();
            partyman_count = kpiBranchInadequateDtoList.get(i).getAmPartymanCount();
            String branchId = kpiBranchInadequateDtoList.get(i).getCtId();
            // 组织中没有党员，则直接继续，不计算

            if (partyman_count == null || partyman_count == 0) {
                log.info("规则id:18082000201 组织不健全 " + kpiBranchInadequateDtoList.get(i).getCtId() + "不存在党员，无需考核");
                continue;
            }
            // 未计算出最少的领导个数，直接忽略不计算
            if (leader_min == null || leader_min == 0) {
                log.info("规则id:18082000201 组织不健全 " + kpiBranchInadequateDtoList.get(i).getCtId() + "不存在最少领导数，无需考核");
                continue;
            }
            // 存在党员，但是没有领导班子，直接考核
            if (leader_ok == null || leader_ok < 1) {
                leader_ok=0;
                assessState = "1";
            } else {
                // 有领导，但数量不足 直接考核
                if (leader_ok < leader_min)
                    assessState = "1";
            }
            // 开始计算规则  检索历史记录中当前单位是不是在90天范围内的历史记录
            List<TKpiSpecialtemp> collect1 = tKpiSpecialtempList.stream()
                    .filter(e -> e.getCtBussId() != null && branchId.equals(e.getCtBussId()))
                    .collect(Collectors.toList());
            // 历史记录中不存在写入的90天内的预警
            if (collect1 != null && collect1.size() < 1) {
                if ("1".equals(assessState)) {
                    // 考核状态，增加记录
                    tempDesc = branchId + " " + kpiBranchInadequateDtoList.get(i).getCtName() +
                            "共有" + partyman_count + "名党员，至少需要" + leader_min + "名班子成员，目前有" + leader_ok + "名";
                    TKpiSpecialtemp insert = new TKpiSpecialtemp();
                    insert.setCtBussId(branchId);
                    insert.setDtBussDate(kpiBranchInadequateDtoList.get(i).getDtDate());
                    insert.setCtDesc(tempDesc);
                    insert.setCtType(ctype);
                    insert.setCtOther3( DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss",nowDate));
                    tKpiSpecialtempMapper.insertTKpiSpecialtemp(insert);
                } else {
                    // 不考核状态， 保持原值0
                }
            } else {
                // 存在记录 ，比较日期 ; 大于3个月就要记录扣分，直接写入减分表
                if ("1".equals(assessState)) {
                    // 存在记录，考核状态下
                    int differDay = 0;
                    try {
                        differDay = getDayDiffer(kpiBranchInadequateDtoList.get(i).getDtDate(), nowDate);
                    } catch (Exception e) {
                        log.info("规则id:18082000201 组织不健全 换届超期日期出现问题");
                        continue;
                    }
                    if (differDay > 91) {
                        // 开始操作 t_kpi_data 表， 分数一定要是负分
                        TKpiData t = new TKpiData();
                        t.setCtBussId(branchId);
                        t.setCtBussPartyorbranchid(branchId);
                        t.setCtBussName(kpiBranchInadequateDtoList.get(i).getCtName());
                        t.setDtBussDate(kpiBranchInadequateDtoList.get(i).getDtDate());
                        t.setDtAuditDate(nowDate);
                        t.setDtCalculateDate(nowDate);
                        if (inserData(collect.get(0), t) == 1) {
                            // 保存tkpidata表返回值如果为1，同时删除 special中的id对应记录
                            TKpiSpecialtemp temp = new TKpiSpecialtemp();
                            temp.setCtBussId(collect1.get(0).getCtBussId());
                            temp.setCtType(ctype);
                            List<TKpiSpecialtemp> tKpiSpecialtemps = tKpiSpecialtempMapper.selectTKpiSpecialtempList(temp);
                            tKpiSpecialtemps.forEach(item -> tKpiSpecialtempMapper.deleteTKpiSpecialtempById(item.getId()));
                        }
                    } else {
                        // 仍然在91天内，状态不发生变化，继续
                    }
                } else {
                    // 资料补全了，不需要考核了
                    TKpiSpecialtemp temp = new TKpiSpecialtemp();
                    temp.setCtBussId(collect1.get(0).getCtBussId());
                    temp.setCtType(ctype);
                    List<TKpiSpecialtemp> tKpiSpecialtemps = tKpiSpecialtempMapper.selectTKpiSpecialtempList(temp);
                    tKpiSpecialtemps.forEach(item -> tKpiSpecialtempMapper.deleteTKpiSpecialtempById(item.getId()));
                }
            }
            //循环结束
        }
    }


    /**
     * 私有： 2.2 实时计算-加分-培养发展 规则id:18082000320 18082000334 18082000336
     * 18082000320 加分
     * 18082000334 扣分 发展党员每个环节逾期1月，不得分。
     * 18082000336 扣分 发展党员每个环节逾期2月，每环节扣1分
     */
    private void calculatePartymanDevelop() {
        // 获取特列规则
        getSpecicalList();
        // 规则id
        String ctype = "18082000320";
        // 当前的基础配置
        TKpiBase tKpiBase = null;
        final Optional<TKpiBase> first1 = tKpiBaseSpecialList.stream().filter(e -> "18082000320".equals(e.getCtId())).findFirst();
        if (first1.isPresent())
            tKpiBase = first1.get();
        else {
            log.info("规则id:18082000320 党员培养发展规则未启用");
            return;
        }
        // 获取党员发展上次计算到的最大日期
        Date maxDate = DateUtils.parseDate("2022-01-01");
        TMisBaseJob tMisBaseJob = tMisBaseJobMapper.selectTMisBaseJobById(99L);
        if (tMisBaseJob == null) {
            tMisBaseJob = new TMisBaseJob();
            tMisBaseJob.setAmId(99L);
            tMisBaseJob.setCtBussType("develop");
            tMisBaseJob.setCtBussName("党员发展最大日期");
            tMisBaseJob.setDtMaxDate(maxDate);
            tMisBaseJobMapper.insertTMisBaseJob(tMisBaseJob);
        } else {
            if (tMisBaseJob.getDtMaxDate() != null)
                maxDate = tMisBaseJob.getDtMaxDate();
        }
        // 获取提取发展党员的最新数据sql,并获取数据
        String sql = tKpiBase.getCtSqlSelect();
        sql = sql.replace("[ct_id_p]", DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", maxDate));
        List<TKpiData> developList = tKpiDataMapper.selectTKpiDataBySql(sql);
        if (developList == null || developList.size() < 1) {
            log.info("规则id:18082000320 党员培养发展规则未匹配到任何党员");
            return;
        }
        String bussName = "";
        // 开始匹配对应的数据
        for (int i = 0; i < developList.size(); i++) {
            // 查看更新日期； 和业务日期；
            if (developList.get(i).getDtAuditDate() == null || developList.get(i).getDtBussDate() == null) {
                log.info("规则id:18082000320 党员培养发展 " + developList.get(i).getCtBussName() + "党员相关日期为空，不计算");
                continue;
            }
            // 业务日期和更新日期（签字日期）的差值
            try {
                int days = getDayDiffer(developList.get(i).getDtBussDate(), developList.get(i).getDtAuditDate());
                if (days <= 30) {
                    // 30天加分 18082000320
                    ctype = "18082000320";
                    bussName = developList.get(i).getCtBussName();
                    // 设置基础配置
                    Optional<TKpiBase> first = tKpiBaseSpecialList.stream().filter(e -> e.getCtId() != null && "18082000320".equals(e.getCtId())).findFirst();
                    if (first.isPresent()) {
                        tKpiBase = first.get();
                    } else {
                        continue;
                    }
                } else if (days > 30 && days <= 60) {
                    // 30-60没有积分 18082000334
                    ctype = "18082000334";
                    bussName = developList.get(i).getCtBussName() + " 逾期30天小于60天，不积分";
                    // 设置基础配置
                    Optional<TKpiBase> first = tKpiBaseSpecialList.stream().filter(e -> e.getCtId() != null && "18082000334".equals(e.getCtId())).findFirst();
                    if (first.isPresent()) {
                        tKpiBase = first.get();
                    } else {
                        continue;
                    }
                } else {
                    // 大于60天扣分拉  18082000336
                    ctype = "18082000336";
                    bussName = developList.get(i).getCtBussName() + " 逾期60天以上";
                    // 设置基础配置
                    Optional<TKpiBase> first = tKpiBaseSpecialList.stream().filter(e -> e.getCtId() != null && "18082000336".equals(e.getCtId())).findFirst();
                    if (first.isPresent()) {
                        tKpiBase = first.get();
                    } else {
                        continue;
                    }
                }
            } catch (Exception e) {
                //卧槽，日期竟然出错了
                log.info(e.getMessage());
            }
            // 创建新增行变量
            TKpiData curTKpiData = new TKpiData();
            // 查询该党员该步骤是否已经追加了积分(按照党员id 和步骤编号 以及规则编号）
            sql = "select * from t_kpi_data where ct_rule_id in ('18082000320','18082000334','18082000336') " +
                    "and ct_fun_no='" + developList.get(i).getCtFunNo() + "' " +
                    "and ct_buss_id='" + developList.get(i).getCtId() + "' ";
            // 理论上最多只有一行
            List<TKpiData> tKpiDataList = tKpiDataMapper.selectTKpiDataBySql(sql);
            if (tKpiDataList != null && tKpiDataList.size() > 0) {
                curTKpiData = tKpiDataList.get(0);
            }
            // 开始追加数据
            curTKpiData.setCtBussId(developList.get(i).getCtId()); //党员id
            curTKpiData.setCtRuleId(ctype);
            curTKpiData.setCtBussPartyorbranchid(developList.get(i).getCtBussPartyorbranchid()); // 组织id
            curTKpiData.setCtFunNo(developList.get(i).getCtFunNo());
            curTKpiData.setCtBussName(bussName);
            // -- 业务日期
            curTKpiData.setDtBussDate(developList.get(i).getDtBussDate());
            // -- 签字日期
            curTKpiData.setDtAuditDate(developList.get(i).getDtAuditDate());
            inserData(tKpiBase, curTKpiData);
            // 追加数据成功则直接修改日期 就是签字的日期
            saveDevelopMaxDate(developList.get(i).getDtAuditDate());
        }
    }

    /**
     * 公用： 按照规则id,去增加或者减少分数
     * @param ruleId 规则id
     * @param curYear 传递年度
     * @param ctIdP 传递业务单据id， 可以为空，可以与年度相同
     * @param checkHis true校验是否存在历史数据，存在则忽略，否则就新增数据
     * @param defaultData 新增数据需要提交的默认值
     */
    private void calculateYearPubic(String ruleId,String curYear,String ctIdP ,Boolean checkHis,TKpiData defaultData) {
        // 拉取特例计算的所有规则
        getSpecicalList();
        // 基础配置
        TKpiBase tKpiBase = null;
        //如果规则表中不包含，则不进行运算
        Optional<TKpiBase> first = tKpiBaseSpecialList.stream().filter(e -> e.getCtId() != null && ruleId.equals(e.getCtId())).findFirst();
        if (first.isPresent()) {
            tKpiBase = first.get();
        } else {
            log.info("规则id:" + ruleId + " 不存在，无需计算");
            return;
        }
        // 设置提示临时变量
        String descMsg = "规则id:" + ruleId + tKpiBase.getCtScoreName();
        // 积分为0不计算
        if (tKpiBase.getAmScore() == 0) {
            log.info(descMsg + " 积分为0，无需计算");
            return;
        }
        // 校验sql语句
        String sql = tKpiBase.getCtSqlSelect();
        if (StringUtils.isEmpty(sql)) {
            log.info(descMsg + " sql未配置");
            return;
        }
        // 替换sql变量
        sql = sql.replace("[ct_id_p]", ctIdP);
        List<TKpiData> tKpiDataList = tKpiDataMapper.selectTKpiDataBySql(sql);
        if (tKpiDataList == null || tKpiDataList.size() < 1) {
            log.info(descMsg + " 未查询到数据");
            return;
        }
        // 查询是否存在历史数据， 存在历史数据，则忽略增加
        List<TKpiData> tKpiDataListHis = null;
        if (checkHis) {
            sql = "select * from t_kpi_data where ct_year='" + curYear + "' and ct_rule_id='" + ruleId + "'";
            tKpiDataListHis = tKpiDataMapper.selectTKpiDataBySql(sql);
        }
        // 处理数据
        Boolean ifInsert = true;
        for (TKpiData tKpiData : tKpiDataList) {
            // 是否校验历史数据
            if (checkHis) {
                // 遍历数据，存在则继续；否则新增
                Optional<TKpiData> firstHis = tKpiDataListHis.stream().filter(e -> e.getCtBussId() != null && e.getCtBussId().equals(tKpiData.getCtBussId())).findFirst();
                if (firstHis.isPresent()) {
                    ifInsert = false;
                }
            }
            // 是否追加数据
            if (!ifInsert) {
                continue;
            }
            // 是否存在默认值，存在默认值，则需要设置用户给定的值
            if (defaultData != null) {
                // 形成的新数据
                Class<?> newclz = tKpiData.getClass();
                Field newField = null;
                // 需要替换的数据
                Class<?> clz = defaultData.getClass();
                Field[] fields = clz.getDeclaredFields();
                for (Field field : fields) {
                    if (field.getName().equals("serialVersionUID")) continue;
                    try {
                        field.setAccessible(true);
                        if (field.get(defaultData) == null) continue;
                        Object val = field.get(defaultData);
                        if (val == null) continue;
                        //获取默认值，并设置到新对象
                        newField = newclz.getDeclaredField(field.getName());
                        newField.setAccessible(true);
                        if (field.getType().getName().contains("String")) {
                            newField.set(tKpiData, (String) val);
                        } else if (field.getType().getName().contains("Date")) {
                            newField.set(tKpiData, (Date) val);
                        } else if (field.getType().getName().contains("Double")) {
                            newField.set(tKpiData, (Double) val);
                        }
                    } catch (Exception e) {
                        log.error("获取属性异常：{}", e);// 天才的想法错了，这是个悲剧
                    }
                }
            }
            inserData(tKpiBase, tKpiData);
        }
    }

    /**
     * 公用：新增data表数据，如果为减分，则值为负数
     *
     * @param tKpiBase 基础库数据
     * @param tKpiData 保存的数据
     *                 return int
     */
    private int inserData(TKpiBase tKpiBase, TKpiData tKpiData) {
        if (tKpiData == null || tKpiBase == null)
            return 0;
        // 新增数据
        // 业务类型 （01 党员 02 组织)
        tKpiData.setCtType(tKpiBase.getCtType());
        // 隶属功能和类型
        // 功能： 如果未传递，则设置规则中的功能号，否则保留元数据
        if (StringUtils.isEmpty(tKpiData.getCtFunNo()))
            tKpiData.setCtFunNo(tKpiBase.getCtBussFunno());
        // 类型
        tKpiData.setCtFunType(tKpiBase.getCtBussType());
        // 获取年度和月份
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(tKpiData.getDtBussDate());
        if (StringUtils.isEmpty(tKpiData.getCtYear()))
            tKpiData.setCtYear("" + calendar.get(Calendar.YEAR));
        if (StringUtils.isEmpty(tKpiData.getCtMonth()))
            tKpiData.setCtMonth((calendar.get(Calendar.MONTH) + 1 > 9) ? ("" + (calendar.get(Calendar.MONTH) + 1)) : ("0" + (calendar.get(Calendar.MONTH) + 1)));
        // 分值类型
        tKpiData.setCtScoresType(tKpiBase.getCtScoreType());
        // 计算类型
        tKpiData.setCtCalculateType(tKpiBase.getCtCountType());
        // 分值
        tKpiData.setAmScores(tKpiBase.getAmScore());
        // 规则id
        if (StringUtils.isEmpty(tKpiData.getCtRuleId()))
            tKpiData.setCtRuleId(tKpiBase.getCtId());
        // 签字日期
        if (tKpiData.getDtAuditDate() == null)
            tKpiData.setDtAuditDate(DateUtils.getNowDate());
        // 计算日期
        tKpiData.setDtCalculateDate(DateUtils.getNowDate());
        // 好像没有啥用的字段，赋值“”
        tKpiData.setCtYearmtype("");
        // 重要的是id
        if (StringUtils.isEmpty(tKpiData.getCtId())) {
            // -- 不存在id ，创建新的，并调用增加数据接口
            tKpiData.setCtId(IdUtils.simpleUUID());
            return tKpiDataMapper.insertTKpiData(tKpiData);
        } else {
            // -- 数据存在， 更新即可。
            return tKpiDataMapper.updateTKpiData(tKpiData);
        }
    }

    /**
     * 公用：根据功能号提取对应的考核规则，
     *
     * @param funNo
     * @return
     */
    private List<TKpiBase> getKpiBaseWithFunno(String funNo) {
        // 只取启动并且是自动计算的规则
        TKpiBase tKpiBase = new TKpiBase();
        tKpiBase.setCtBussFunno(funNo);
        tKpiBase.setCtIfEnable("1");
        tKpiBase.setCtCountType("自动计算");
        // 理论上添加类型， 01党员先锋指数 02 组织堡垒指数
        // tKpiBase.setCtType("02");
        List<TKpiBase> tKpiBaseList = tKpiBaseMapper.selectTKpiBaseList(tKpiBase);
        return tKpiBaseList;
    }

    /**
     * 公用：获取特例计算的所有规则
     */
    private void getSpecicalList() {
        if (tKpiBaseSpecialList != null)
            return;
        // 只取启动并且是自动计算的规则
        TKpiBase tKpiBase = new TKpiBase();
        tKpiBase.setCtIfEnable("1");
        tKpiBase.setCtCountType("特例计算");
        // 理论上添加类型， 01党员先锋指数 02 组织堡垒指数 , 因为党员指数未启用，所以不过滤了
        // tKpiBase.setCtType("02");
        tKpiBaseSpecialList = tKpiBaseMapper.selectTKpiBaseList(tKpiBase);
    }

    /**
     * 公用：更新tmisbasejob中党员发展计算到的最大日期
     * @param maxDate
     */
    private void saveDevelopMaxDate(Date maxDate) {
        try {
            TMisBaseJob temp = new TMisBaseJob();
            temp.setAmId(99L);
            temp.setCtBussType("develop");
            temp.setCtBussName("党员发展最大日期");
            temp.setDtMaxDate(maxDate);
            tMisBaseJobMapper.updateTMisBaseJob(temp);
        } catch (Exception e) {
        }
    }

    /**
     * 公用：月度第一天 > 根据当前日期获取月度第一天
     *
     * @param date 当前日期
     * @return
     */
    public String getCurrentMonthDateBegin(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        calendar.add(Calendar.MONTH, 0);
        // return calendar.getTime();
        int m = calendar.get(Calendar.MONTH) + 1;
        String ms = m > 9 ? "" + m : "0" + m;
        return calendar.get(Calendar.YEAR) + "-" + ms + "-01 00:00:00";
    }

    /**
     * 公用：月度最后一天 > 根据当前日期获取月度第一天
     *
     * @param date 当前日期
     * @return
     */
    public String getCurrentMonthDateEnd(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int lastDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        calendar.set(Calendar.DAY_OF_MONTH, lastDay);
        // return calendar.getTime();
        int m = calendar.get(Calendar.MONTH) + 1;
        String ms = m > 9 ? "" + m : "0" + m;
        int d = lastDay;
        String ds = d > 9 ? "" + d : "0" + d;
        return calendar.get(Calendar.YEAR) + "-" + ms + "-" + ds + " 23:59:59";
    }

    /**
     * 公用： 季度的开始日期 > 根据当前日期获取季度第一天
     *
     * @param date
     * @return
     */
    public String getCurrentQuarterDateBegin(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int month = calendar.get(Calendar.MONTH);
        int quarter = month / 3 + 1;
        int startMonth = 1;
        if (quarter == 2) {
            startMonth = 4;
        } else if (quarter == 3) {
            startMonth = 7;
        } else if (quarter == 4) {
            startMonth = 10;
        }
        // calendar.set(Calendar.MONTH, startMonth - 1);
        // calendar.set(Calendar.DAY_OF_MONTH, 1);
        // return calendar.getTime();
        int m = startMonth;
        String ms = m > 9 ? "" + m : "0" + m;
        return calendar.get(Calendar.YEAR) + "-" + ms + "-01 00:00:00";
    }

    /**
     * 公用：季度的最后一天 > 根据当前日期获取季度最后一天
     *
     * @param date
     * @return
     */
    public String getCurrentQuarterDateEnd(Date date) {
        Calendar endCalendar = Calendar.getInstance();
        endCalendar.setTime(date);
        //计算季度数：由于月份从0开始，即1月份的Calendar.MONTH值为0,所以计算季度的第三个月份只需 月份 / 3 * 3 + 2
        endCalendar.set(Calendar.MONTH, (((int) endCalendar.get(Calendar.MONTH)) / 3) * 3 + 2);
        endCalendar.set(Calendar.DAY_OF_MONTH, endCalendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd 23:59:59");
        return dateFormat.format(endCalendar.getTime());
    }

    /**
     * 公用：两个日期之间的天数
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return
     * @throws ParseException 异常参数
     */
    private int getDayDiffer(Date startDate, Date endDate) throws ParseException {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        long startDateTime = dateFormat.parse(dateFormat.format(startDate)).getTime();
        long endDateTime = dateFormat.parse(dateFormat.format(endDate)).getTime();
        return (int) ((endDateTime - startDateTime) / (1000 * 3600 * 24));
    }
}
